﻿namespace NJQuery {
	using System;
	using System.Web.UI;
	using System.Web.UI.WebControls;
	using System.ComponentModel;
	using System.Globalization;
	using System.Collections.Specialized;
	using System.Diagnostics.CodeAnalysis;

	/// <summary>
	/// Server control used to generate a <see href="http://jqueryui.com/demos/dialog/">jQuery UI Dialog widget</see>.
	/// </summary>
	[ParseChildren(true), PersistChildren(false)]
	public class Dialog : DialogFoundation, IPostBackDataHandler, IPostBackEventHandler {


		#region Constants: Property defaults

		private const bool defaultAutoOpen = true;
		private const bool defaultBgIFrame = false;
		private const bool defaultCloseOnEscape = true;
		private const bool defaultDraggable = true;
		private const int defaultMinHeight = 150;
		private const int defaultMinWidth = 150;
		private const bool defaultModal = false;
		private const string defaultPositionString = "Center";
		private const bool defaultResizable = true;
		private const bool defaultStack = true;
		private const int defaultWidth = 300;
		private const int defaultZIndex = 1000;
		private const bool defaultIsOpen = false;

		#endregion


		#region Static Readonly Fields: Property defaults

		private static readonly DialogAnimation defaultHide = DialogAnimation.Immediate;
		private static readonly Position defaultPosition = NJQuery.Position.Center;
		private static readonly DialogAnimation defaultShow = DialogAnimation.Immediate;

		#endregion


		#region Fields

		private ITemplate content;
		private GroupControlContainer contentContainer;
		private DialogButtonCollection buttonCollection;
		private bool isOpenChanged;
		private bool heightChanged;
		private bool positionChanged;
		private bool widthChanged;
		private bool isOpen;
		private bool openPending;
		private bool closePending;
		private IEffect showControl;
		private IEffect hideControl;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="Dialog"/> class.
		/// </summary>
		public Dialog() { }

		#endregion


		#region Properties: Widget initialization options

		/// <summary>
		/// When AutoOpen is true the dialog will open automatically upon initialization. If false it will stay hidden until .dialog("open") is called on it via JavaScript.
		/// This property maps to the "autoOpen" option in combination with the <see cref="IsOpen"/> property when the jQuery UI widget is initialized, and is adjusted on postback to achieve logical effect.
		/// </summary>
		/// <seealso cref="IsOpen"/>
		[DefaultValue(defaultAutoOpen), Category("Behavior")]
		public virtual bool AutoOpen {
			get { return ViewState["AutoOpen"] as bool? ?? defaultAutoOpen; }
			set { ViewState["AutoOpen"] = value; }
		}

		/// <summary>
		/// When true, the bgiframe plugin will be used to fix the issue in IE6 where select boxes show on top of other elements regardless of zIndex. Requires including the bgiframe plugin. Future versions may not require a separate plugin.
		/// This property maps directly to the "bgiframe" option when the jQuery UI widget is initialized.
		/// </summary>
		[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Bg", Justification = "This property name comes from the client.  Wouldn't otherwise use abbreviation for Background IFrame.")]
		[DefaultValue(defaultBgIFrame), Category("Behavior")]
		public virtual bool BgIFrame {
			get { return ViewState["BgIFrame"] as bool? ?? defaultBgIFrame; }
			set { ViewState["BgIFrame"] = value; }
		}

		/// <summary>
		/// Specifies which buttons should be displayed on the dialog.
		/// This property maps directly to the "buttons" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(DialogButtonCollection), null), Category("Misc"), PersistenceMode(PersistenceMode.InnerProperty)]
		public virtual DialogButtonCollection Buttons {
			get {
				EnsureChildControls();
				return buttonCollection;
			}
		}

		/// <summary>
		/// Specifies whether the dialog should close when it has focus and the user presses the escape (ESC) key.
		/// This property maps directly to the "closeOnEscape" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultCloseOnEscape), Category("Behavior")]
		public virtual bool CloseOnEscape {
			get { return ViewState["CloseOnEscape"] as bool? ?? defaultCloseOnEscape; }
			set { ViewState["CloseOnEscape"] = value; }
		}

		/// <summary>
		/// The specified class name(s) will be added to the dialog for additional theming.
		/// This property maps directly to the "dialogClass" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Appearance"), CssClassProperty]
		public virtual string DialogClass {
			get { return ViewState["DialogClass"] as string ?? string.Empty; }
			set { ViewState["DialogClass"] = value; }
		}

		/// <summary>
		/// If set to true the dialog will be draggable by the titlebar.
		/// This property maps directly to the "draggable" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultDraggable), Category("Behavior")]
		public virtual bool Draggable {
			get { return ViewState["Draggable"] as bool? ?? defaultDraggable; }
			set { ViewState["Draggable"] = value; }
		}

		/// <summary>
		/// The height of the dialog, in pixels (null for Auto).
		/// This property maps to the "height" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(int?), null), Category("Layout")]
		public new virtual int? Height {
			get { return ViewState["Height"] as int?; }
			set { ViewState["Height"] = value; }
		}

		/// <summary>
		/// The effect to be used when the dialog is closed.
		/// This property maps directly to the "hide" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(DialogAnimation), ""), Category("Behavior")]
		public virtual DialogAnimation Hide {
			get { return ViewState["Hide"] as DialogAnimation? ?? defaultHide; }
			set { ViewState["Hide"] = value; }
		}

		//TODO: Make sure Layout is the right category for these properties
		/// <summary>
		/// The maximum height to which the dialog can be resized, in pixels.
		/// This property maps directly to the "maxHeight" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(null), Category("Layout")]
		public virtual int? MaxHeight {
			get { return ViewState["MaxHeight"] as int?; }
			set { ViewState["MaxHeight"] = value; }
		}

		/// <summary>
		/// The maximum width to which the dialog can be resized, in pixels.
		/// This property maps directly to the "maxWidth" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(null), Category("Layout")]
		public virtual int? MaxWidth {
			get { return ViewState["MaxWidth"] as int?; }
			set { ViewState["MaxWidth"] = value; }
		}

		/// <summary>
		/// The minimum height to which the dialog can be resized, in pixels.
		/// This property maps directly to the "minHeight" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultMinHeight), Category("Layout")]
		public virtual int MinHeight {
			get { return ViewState["MinHeight"] as int? ?? defaultMinHeight; }
			set { ViewState["MinHeight"] = value; }
		}

		/// <summary>
		/// The minimum width to which the dialog can be resized, in pixels.
		/// This property maps directly to the "minWidth" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultMinWidth), Category("Layout")]
		public virtual int MinWidth {
			get { return ViewState["MinWidth"] as int? ?? defaultMinWidth; }
			set { ViewState["MinWidth"] = value; }
		}

		/// <summary>
		/// If set to true, the dialog will have modal behavior; other items on the page will be disabled (i.e. cannot be interacted with). Modal dialogs create an overlay below the dialog but above other page elements.
		/// This property maps directly to the "modal" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultModal), Category("Behavior")]
		public virtual bool Modal {
			get { return ViewState["Modal"] as bool? ?? defaultModal; }
			set { ViewState["Modal"] = value; }
		}

		/// <summary>
		/// Specifies where the dialog should be displayed.
		/// This property maps directly to the "position" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(typeof(Position), defaultPositionString), Category("Layout")]
		public virtual Position Position {
			get { return ViewState["Position"] as Position? ?? defaultPosition; }
			set { ViewState["Position"] = value; }
		}

		/// <summary>
		/// If set to true the dialog will be resizeable.
		/// This property maps directly to the "resizable" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultResizable), Category("Behavior")]
		public virtual bool Resizable {
			get { return ViewState["Resizable"] as bool? ?? defaultResizable; }
			set { ViewState["Resizable"] = value; }
		}

		/// <summary>
		/// The effect to be used when the dialog is opened.
		/// This property maps to the "show" option when the jQuery UI widget is initialized, with its presence adjusted to achieve desired postback behavior.
		/// </summary>
		/// <seealso cref="IsOpen"/>
		[DefaultValue(typeof(DialogAnimation), ""), Category("Behavior")]
		public virtual DialogAnimation Show {
			get { return ViewState["Show"] as DialogAnimation? ?? defaultShow; }
			set { ViewState["Show"] = value; }
		}

		/// <summary>
		/// Specifies whether the dialog will stack on top of other dialogs. This will cause the dialog to move to the front of other dialogs when it gains focus.
		/// This property maps directly to the "stack" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultStack), Category("Behavior")]
		public virtual bool Stack {
			get { return ViewState["Stack"] as bool? ?? defaultStack; }
			set { ViewState["Stack"] = value; }
		}

		/// <summary>
		/// Specifies the title of the dialog.
		/// This property is HTML encoded and mappped to the "title" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Appearance")]
		public virtual string Title {
			get { return ViewState["Title"] as string ?? string.Empty; }
			set { ViewState["Title"] = value; }
		}

		/// <summary>
		/// The width of the dialog, in pixels.
		/// This property maps directly to the "width" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultWidth), Category("Layout")]
		public new virtual int Width {
			get { return ViewState["Width"] as int? ?? defaultWidth; }
			set { ViewState["Width"] = value; }
		}

		/// <summary>
		/// The starting z-index for the dialog.
		/// This property maps directly to the "zIndex" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(defaultZIndex), Category("Layout")]
		public virtual int ZIndex {
			get { return ViewState["ZIndex"] as int? ?? defaultZIndex; }
			set { ViewState["ZIndex"] = value; }
		}

		/// <summary>
		/// This event is triggered when a dialog attempts to close. If the beforeclose event handler (callback function) returns false, the close will be prevented.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "beforeclose" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientBeforeClose {
			get { return ViewState["OnClientBeforeClose"] as string ?? string.Empty; }
			set { ViewState["OnClientBeforeClose"] = value; }
		}

		/// <summary>
		/// This event is triggered when dialog is opened.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps to the "open" option when the jQuery UI widget is initialized.
		/// The property appends itself to code used persist the <see cref="IsOpen"/> property and to keep the DOM location of the dialog within the ASP.NET form and maps to the "open" option with the jQuery widget is initalized.
		/// </summary>
		/// <seealso cref="IsOpen"/>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientOpen {
			get { return ViewState["OnClientOpen"] as string ?? string.Empty; }
			set { ViewState["OnClientOpen"] = value; }
		}

		/// <summary>
		/// This event is triggered when the dialog gains focus.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "focus" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientFocus {
			get { return ViewState["OnClientFocus"] as string ?? string.Empty; }
			set { ViewState["OnClientFocus"] = value; }
		}

		/// <summary>
		/// This event is triggered at the beginning of the dialog being dragged.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "dragStart" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientDragStart {
			get { return ViewState["OnClientDragStart"] as string ?? string.Empty; }
			set { ViewState["OnClientDragStart"] = value; }
		}

		/// <summary>
		/// Supply a callback function to handle the drag event as an init option. 
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "drag" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientDrag {
			get { return ViewState["OnClientDrag"] as string ?? string.Empty; }
			set { ViewState["OnClientDrag"] = value; }
		}

		/// <summary>
		/// This event is triggered after the dialog has been dragged.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// The property appends itself to code used to persist the <see cref="Position"/> property and maps to the "dragStop" option with the jQuery widget is initalized.
		/// </summary>
		/// <seealso cref="Position"/>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientDragStop {
			get { return ViewState["OnClientDragStop"] as string ?? string.Empty; }
			set { ViewState["OnClientDragStop"] = value; }
		}

		/// <summary>
		/// This event is triggered at the beginning of the dialog being resized.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "resizeStart" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientResizeStart {
			get { return ViewState["OnClientResizeStart"] as string ?? string.Empty; }
			set { ViewState["OnClientResizeStart"] = value; }
		}

		/// <summary>
		/// This event is triggered when the dialog is resized.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// This property maps directly to the "resize" option when the jQuery UI widget is initialized.
		/// </summary>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientResize {
			get { return ViewState["OnClientResize"] as string ?? string.Empty; }
			set { ViewState["OnClientResize"] = value; }
		}

		/// <summary>
		/// This event is triggered after the dialog has been resized.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// The property appends itself to code used to persist the <see cref="Position"/>, <see cref="Width"/> and <see cref="Height"/> properties and maps to the "resizeStop" option with the jQuery widget is initalized.
		/// </summary>
		/// <seealso cref="Position"/>
		/// <seealso cref="Width"/>
		/// <seealso cref="Height"/>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientResizeStop {
			get { return ViewState["OnClientResizeStop"] as string ?? string.Empty; }
			set { ViewState["OnClientResizeStop"] = value; }
		}

		/// <summary>
		/// Supply a callback function to handle the close event as an init option.
		/// This is a client-side JavaScript event with the signature function(event,ui).
		/// The property appends itself to code used to persist the <see cref="IsOpen"/> property and maps to the "close" option with the jQuery widget is initalized.
		/// </summary>
		/// <seealso cref="IsOpen"/>
		[DefaultValue(""), Category("Behavior")]
		public virtual string OnClientClose {
			get { return ViewState["OnClientClose"] as string ?? string.Empty; }
			set { ViewState["OnClientClose"] = value; }
		}

		#endregion


		#region Properties: Templates

		/// <summary>
		/// Gets or sets the template that defines the content of the <see cref="Dialog"/> control.
		/// </summary>
		[TemplateInstance(TemplateInstance.Single), PersistenceMode(PersistenceMode.InnerProperty), Browsable(false)]
		public ITemplate Content {
			get { return content; }
			set { content = value; }
		}

		/// <summary>
		/// Gets a control object to which you can programmatically add child controls for <see cref="Content"/>.
		/// </summary>
		[Browsable(false)]
		public Control ContentContainer {
			get {
				EnsureChildControls();
				return contentContainer;
			}
		}

		#endregion


		#region Properties: Widget Method

		/// <summary>
		/// Gets whether the dialog is currently open.  If <see cref="AutoOpen"/> is set to true, this value will change on first render.
		/// </summary>
		/// <seealso cref="AutoOpen"/>
		public virtual bool IsOpen {
			get { return isOpen; }
		}

		/// <summary>
		/// Gets whether or not an open action will happen on next render.
		/// </summary>
		public virtual bool OpenPending {
			get { return openPending; }
		}

		/// <summary>
		/// Gets whether or not a close action will happen on next render.
		/// </summary>
		public virtual bool ClosePending {
			get { return closePending; }
		}

		#endregion


		#region Properties: Hidden Fields

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the <see cref="IsOpen"/> property.
		/// </summary>
		public virtual string IsOpenClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_IsOpen", ClientID); }
		}

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the <see cref="Position"/> property.
		/// </summary>
		public virtual string PositionClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_Position", ClientID); }
		}

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the <see cref="Width"/> property.
		/// </summary>
		public virtual string WidthClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_Width", ClientID); }
		}

		/// <summary>
		/// Gets the rendered element ID of the hidden field used to round-trip the <see cref="Height"/> property.
		/// </summary>
		public virtual string HeightClientId {
			get { return string.Format(CultureInfo.InvariantCulture, "__{0}_Height", ClientID); }
		}

		#endregion


		#region Properties: Protected

		/// <summary>
		/// Gets whether or not the show animations/events should be set after initialization in order to make a postback not behave like a show.
		/// </summary>
		protected virtual bool UseDelayedShowInit {
			get { return ClosePending || (Page.IsPostBack && !OpenPending); }
		}

		#endregion


		#region Events

		/// <summary>
		/// Occurs on postback when the <see cref="IsOpen"/> property value has changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler IsOpenChanged;

		/// <summary>
		/// Occurs on postback when the <see cref="Position"/> property value has changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler PositionChanged;

		/// <summary>
		/// Occurs on postback when the <see cref="Width"/> property value has changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler WidthChanged;

		/// <summary>
		/// Occurs on postback when the <see cref="Height"/> property value has changed since previous render.
		/// </summary>
		[Category("Action")]
		public event EventHandler HeightChanged;

		#endregion


		#region Methods: Event hooks

		/// <summary>
		/// Raises the <see cref="IsOpenChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnIsOpenChanged(EventArgs e) {
			if(IsOpenChanged != null) IsOpenChanged.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="PositionChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnPositionChanged(EventArgs e) {
			if(PositionChanged != null) PositionChanged.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="WidthChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnWidthChanged(EventArgs e) {
			if(WidthChanged != null) WidthChanged.Invoke(this, e);
		}

		/// <summary>
		/// Raises the <see cref="HeightChanged"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected virtual void OnHeightChanged(EventArgs e) {
			if(HeightChanged != null) HeightChanged.Invoke(this, e);
		}

		#endregion


		#region Methods: Widget methods

		/// <summary>
		/// Open the dialog.
		/// This method will cause a client-side jQuery call to the widget's "open" method to be rendered and executed at page load.
		/// </summary>
		public virtual void Open() {
			if(isOpen) return;
			openPending = true;
		}

		/// <summary>
		/// Close the dialog.
		/// This method will cause a client-side jQuery call to the widget's "close" method to be rendered and executed at page load.
		/// </summary>
		public virtual void Close() {
			if(!isOpen) return;
			closePending = true;
		}

		#endregion


		#region Methods: Child controls

		/// <summary>
		/// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
		/// </summary>
		protected override void CreateChildControls() {
			base.CreateChildControls();

			if(contentContainer == null) {
				contentContainer = CreateContentContainer();
				GroupedControls.AddContainer(contentContainer);
			}

			if(buttonCollection == null) {
				GroupControlContainer buttonContainer = CreateButtonContainer();
				GroupedControls.AddContainer(buttonContainer);
				buttonCollection = CreateButtons(buttonContainer);
			}

		}

		/// <summary>
		/// Creates the control object to which the user can programmatically add child controls for <see cref="Content"/>.
		/// </summary>
		/// <returns>The <see cref="GroupControlContainer"/> to group the child controls.</returns>
		protected virtual GroupControlContainer CreateContentContainer() {
			return new GroupControlContainer(this);
		}

		/// <summary>
		/// Creates a <see cref="GroupControlContainer"/> object to contain the grouped child controls for the <see cref="Buttons"/> property.
		/// </summary>
		/// <returns></returns>
		protected virtual GroupControlContainer CreateButtonContainer() {
			return new GroupControlContainer(this);
		}

		/// <summary>
		/// Creates a <see cref="DialogButtonCollection"/> object to be used as the <see cref="Buttons"/> property.
		/// </summary>
		/// <returns>A new <see cref="DialogButtonCollection"/> instance.</returns>
		protected virtual DialogButtonCollection CreateButtons(GroupControlContainer container) {
			return new DialogButtonCollection(container);
		}

		#endregion


		#region Methods: Rendering hooks

		/// <summary>
		/// Allows a jQuery server control to reference scripts and stylesheets necessary to render their client markup and widgets.
		/// </summary>
		/// <param name="manager">The <see cref="Manager"/> used to handle style and script referencing in addition to unified generation of load and ready scripts.</param>
		protected override void Register(Manager manager) {
			bool resizable = Resizable;

			manager.RegisterClientScript(ClientScript.Core);
			manager.RegisterClientScript(ClientScript.UICore);
			manager.RegisterClientScript(ClientScript.UIWidgetFactory);
			manager.RegisterClientScript(ClientScript.UIPositionUtility);
			manager.RegisterClientScript(ClientScript.UIMouseUtility);
			manager.RegisterClientScript(ClientScript.UIDialogWidget);
			if(Draggable) manager.RegisterClientScript(ClientScript.UIDraggableInteraction);
			if(resizable) manager.RegisterClientScript(ClientScript.UIResizableInteraction);
			manager.RegisterClientScript(ClientScript.BgIFramePlugin);

			manager.RegisterClientStyle(ClientStyle.UICore);
			manager.RegisterClientStyle(ClientStyle.UIDialogWidget);
			if(resizable) manager.RegisterClientStyle(ClientStyle.UIResizableInteraction);
			manager.RegisterTheme();

			manager.RegisterForReadyScript(this);
		}

		/// <summary>
		/// Raises the <see cref="Control.Init"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);

			if(Content != null) Content.InstantiateIn(ContentContainer);

		}

		/// <summary>
		/// Raises the <see cref="Control.PreRender"/> event.
		/// </summary>
		/// <param name="e">The <see cref="EventArgs"/> object that contains the event data.</param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			string showControlId = Show.ControlId;
			if(!string.IsNullOrEmpty(showControlId)) showControl = NamingContainer.FindControl(showControlId) as IEffect;

			string hideControlId = Hide.ControlId;
			if(!string.IsNullOrEmpty(hideControlId)) hideControl = NamingContainer.FindControl(hideControlId) as IEffect;

			if(Visible) Page.RegisterRequiresPostBack(this);
		}

		/// <summary>
		/// Sends server control content to a provided <see cref="HtmlTextWriter"/> object, which writes the content to be rendered to the client.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="writer">A <see cref="HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
		protected override void Render(HtmlTextWriter writer) {
			base.Render(writer);
			
			ClientScriptManager clientScript = Page.ClientScript;
			clientScript.RegisterForEventValidation(new PostBackOptions(this));

			clientScript.RegisterHiddenField(IsOpenClientId, IsOpen.ToString());
			int? height = Height;
			clientScript.RegisterHiddenField(HeightClientId, height.HasValue ? height.Value.ToString(CultureInfo.InvariantCulture) : string.Empty);
			clientScript.RegisterHiddenField(PositionClientId, Position.ToString());
			clientScript.RegisterHiddenField(WidthClientId, Width.ToString(CultureInfo.InvariantCulture));
		}

		/// <summary>
		/// Adds HTML attributes and styles that need to be rendered to the specified <see cref="HtmlTextWriter"/>.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="writer">A <see cref="HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
		protected override void AddAttributesToRender(HtmlTextWriter writer) {

			//TODO: Let's revisit the whole "degradability versus getting rid of flicker issue"  I don't think this falls under UseCssStyling, perhaps a DegradeForNonJs... Or is that a moot point here?
			if(!MasterUseCssStyling)
				writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");

			base.AddAttributesToRender(writer);
		}

		/// <summary>
		/// Renders the jQuery "Ready Event" script required to generate the client-side jQuery widget.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected override void RenderReadyScript(ScriptBuilder script) {
			RenderInitScript(script);
			if(!IsEnabled) RenderDisableScript(script);
			if(OpenPending) RenderOpenMethodScript(script);
			if(ClosePending) RenderCloseMethodScript(script);
			if(UseDelayedShowInit) RenderDelayedShowInitScript(script);
		}

		/// <summary>
		/// Renders the jQuery UI initialization script 
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderInitScript(ScriptBuilder script) {

			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".dialog(");

			PropertyCollection properties = script.CreateProperties();
			AddInitProperties(properties);
			script.RenderProperties(properties);

			script.Append(");");
		}

		/// <summary>
		/// Creates the properties object that will be rendered as the jQuery UI initialization options.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="properties">A <see cref="PropertyCollection"/> object that will become the jQuery UI initialization options.</param>
		protected virtual void AddInitProperties(PropertyCollection properties) {

			bool useDelayedShowInit = UseDelayedShowInit;

			bool autoOpen = Page.IsPostBack ? IsOpen : AutoOpen;
			if(autoOpen != defaultAutoOpen) properties.AddValue("autoOpen", autoOpen);

			bool bgIFrame = BgIFrame;
			if(bgIFrame != defaultBgIFrame) properties.AddValue("bgiframe", bgIFrame);

			DialogButtonCollection buttons = Buttons;
			if(buttons != null && buttons.Count != 0)
				properties.AddValue("buttons", buttons);

			bool closeOnEscape = CloseOnEscape;
			if(closeOnEscape != defaultCloseOnEscape) properties.AddValue("closeOnEscape", closeOnEscape);

			string dialogClass = DialogClass;
			if(!string.IsNullOrEmpty(dialogClass)) properties.AddValue("dialogClass", dialogClass);

			bool draggable = Draggable;
			if(draggable != defaultDraggable) properties.AddValue("draggable", draggable);

			int? height = Height;
			if(height.HasValue) properties.AddValue("height", height.Value);

			DialogAnimation hide = Hide;
			if(hideControl != null) properties.AddValue("hide", hideControl.Effect);
			else if(hide != defaultHide) properties.AddValue("hide", hide);

			int? maxHeight = MaxHeight;
			if(maxHeight.HasValue) properties.AddValue("maxHeight", maxHeight.Value);

			int? maxWidth = MaxWidth;
			if(maxWidth.HasValue) properties.AddValue("maxWidth", maxWidth.Value);

			int minHeight = MinHeight;
			if(minHeight != defaultMinHeight) properties.AddValue("minHeight", minHeight);

			int minWidth = MinWidth;
			if(minWidth != defaultMinWidth) properties.AddValue("minWidth", minWidth);

			bool modal = Modal;
			if(modal != defaultModal) properties.AddValue("modal", modal);

			Position position = Position;
			if(position != defaultPosition) properties.AddValue("position", position);

			bool resizable = Resizable;
			if(resizable != defaultResizable) properties.AddValue("resizable", resizable);

			if(!useDelayedShowInit) {
				DialogAnimation show = Show;
				if(showControl != null) properties.AddValue("show", showControl.Effect);
				else if(show != defaultShow) properties.AddValue("show", show);
			}

			bool stack = Stack;
			if(stack != defaultStack) properties.AddValue("stack", stack);

			string title = Title;
			if(!string.IsNullOrEmpty(title)) properties.AddValue("title", title, true);

			int width = Width;
			if(width != defaultWidth) properties.AddValue("width", width);

			int zIndex = ZIndex;
			if(zIndex != defaultZIndex) properties.AddValue("zIndex", zIndex);

			string onClientBeforeClose = OnClientBeforeClose ?? string.Empty;
			if(onClientBeforeClose.Length != 0) properties.AddScript("beforeclose", onClientBeforeClose); //Yes, the widget option does appear to be "beforeclose" and not "beforeClose".

			ScriptBuilder onOpenScript = properties.CreateChildScript();
			RenderOpenEventScript(onOpenScript, !useDelayedShowInit);
			if(onOpenScript.Length != 0) properties.AddScript("open", onOpenScript);

			string onClientFocus = OnClientFocus ?? string.Empty;
			if(onClientFocus.Length != 0) properties.AddScript("focus", onClientFocus);

			string onClientDragStart = OnClientDragStart ?? string.Empty;
			if(onClientDragStart.Length != 0) properties.AddScript("dragStart", onClientDragStart);

			string onClientDrag = OnClientDrag ?? string.Empty;
			if(onClientDrag.Length != 0) properties.AddScript("drag", onClientDrag);

			ScriptBuilder dragStopScript = properties.CreateChildScript();
			RenderDragStopScript(dragStopScript);
			if(dragStopScript.Length != 0) properties.AddScript("dragStop", dragStopScript);

			string onClientResizeStart = OnClientResizeStart ?? string.Empty;
			if(onClientResizeStart.Length != 0) properties.AddScript("resizeStart", onClientResizeStart);

			string onClientResize = OnClientResize ?? string.Empty;
			if(onClientResize.Length != 0) properties.AddScript("resize", onClientResize);

			ScriptBuilder resizeStopScript = properties.CreateChildScript();
			RenderResizeStopScript(resizeStopScript);
			if(resizeStopScript.Length != 0) properties.AddScript("resizeStop", resizeStopScript);

			ScriptBuilder onCloseScript = properties.CreateChildScript();
			RenderCloseEventScript(onCloseScript);
			if(onCloseScript.Length != 0) properties.AddScript("close", onCloseScript);

		}

		/// <summary>
		/// Renders the client "open" event script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <param name="fullScript">Whether or not to render the entire script versus just enough to maintain compatibility with postback.</param>
		protected virtual void RenderOpenEventScript(ScriptBuilder script, bool fullScript) {

			script.Append("function(event, ui) {");

			script.Indent++;

			//Keep the DOM location within the ASP.NET form
			script.NewLine();
			script.Append("$(this).parent().appendTo(");
			script.AppendValue("#" + Page.Form.ClientID);
			script.Append(");");

			if(fullScript) {

				//Persist IsOpen
				script.NewLine();
				script.RenderIdSelector(IsOpenClientId);
				script.Append(".val('True');");

				//Add custom open event
				string onClientOpen = OnClientOpen ?? string.Empty;
				if(onClientOpen.Length != 0) {
					script.NewLine();
					script.Append("var open = ");
					script.Append(onClientOpen);
					script.Append(";");
					script.Append("open(event, ui);");
				}

			}

			script.Indent--;
			script.NewLine();

			script.Append("}");
		}

		/// <summary>
		/// Renders the client "dragStop" event script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderDragStopScript(ScriptBuilder script) {

			script.Append("function(event, ui) {");

			script.Indent++;

			//Persist Position Property to Server
			script.NewLine();
			script.RenderIdSelector(PositionClientId);
			script.Append(".val(Math.floor(ui.position.left) + ',' + Math.floor(ui.position.top));");

			//Persist Position Property to Client
			script.NewLine();
			script.Append("$(this).dialog('option', 'position', [ui.position.left, ui.position.top]);");

			//Add custom dragStop event
			string onClientDragStop = OnClientDragStop ?? string.Empty;
			if(onClientDragStop.Length != 0) {
				script.NewLine();
				script.Append("var dragStop = ");
				script.Append(onClientDragStop);
				script.Append(";");
				script.NewLine();
				script.Append("dragStop(event, ui);");
			}

			script.Indent--;
			script.NewLine();

			script.Append("}");
		}

		/// <summary>
		/// Renders the client "resizeStop" event script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderResizeStopScript(ScriptBuilder script) {

			script.Append("function(event, ui) {");

			script.Indent++;

			//Persist Position Property to Server
			script.NewLine();
			script.RenderIdSelector(PositionClientId);
			script.Append(".val(Math.floor(ui.position.left) + ',' + Math.floor(ui.position.top));");

			//Persist Width Property to Server
			script.NewLine();
			script.RenderIdSelector(WidthClientId);
			script.Append(".val(ui.size.width);");

			//Persist Height Property To Server
			script.NewLine();
			script.RenderIdSelector(HeightClientId);
			script.Append(".val(ui.size.height);");

			//Persist Position Property to Client
			script.NewLine();
			script.Append("$(this).dialog('option', 'position', [ui.position.left, ui.position.top]);");

			//Persist Width Property to Client
			script.NewLine();
			script.Append("$(this).dialog('option', 'width', ui.size.width);");

			//Persist Height Property to Client
			script.NewLine();
			script.Append("$(this).dialog('option', 'height', ui.size.height);");

			//Add custom resizeStop event
			string onClientResizeStop = OnClientResizeStop ?? string.Empty;
			if(onClientResizeStop.Length != 0) {
				script.NewLine();
				script.Append("var resizeStop = ");
				script.Append(onClientResizeStop);
				script.Append(";");
				script.NewLine();
				script.Append("resizeStop(event, ui);");
			}

			script.Indent--;
			script.NewLine();

			script.Append("}");
		}

		/// <summary>
		/// Renders the client "close" event script.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		protected virtual void RenderCloseEventScript(ScriptBuilder script) {

			script.Append("function(event, ui) {");

			script.Indent++;

			//Persist IsOpen
			script.NewLine();
			script.RenderIdSelector(IsOpenClientId);
			script.Append(".val('False');");

			//Add custom open event
			string onClientClose = OnClientClose ?? string.Empty;
			if(onClientClose.Length != 0) {
				script.NewLine();
				script.Append("var close = ");
				script.Append(onClientClose);
				script.Append(";");
				script.NewLine();
				script.Append("close(event, ui);");
			}

			script.Indent--;
			script.NewLine();

			script.Append("}");
		}

		/// <summary>
		/// Renders a client "disable" method call when the <see cref="WebControl.Enabled"/> property is set to false.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <seealso cref="WebControl.Enabled"/>
		protected virtual void RenderDisableScript(ScriptBuilder script) {

			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".dialog(");
			script.AppendValue("disable");
			script.Append(");");
		}

		/// <summary>
		/// Renders a client "open" method call when a call is made server-side to the <see cref="Open"/> method.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <seealso cref="Open"/>
		protected virtual void RenderOpenMethodScript(ScriptBuilder script) {

			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".dialog(");
			script.AppendValue("open");
			script.Append(");");
		}

		/// <summary>
		/// Renders a client "close" method call when a call is made server-side to the <see cref="Close"/> method.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <seealso cref="Close"/>
		protected virtual void RenderCloseMethodScript(ScriptBuilder script) {

			script.NewLine();

			//The jQuery UI Dialog widget does not like opening and closing in a single breath,
			//which is what happens on a post-back close.  This takes care of that issue.
			script.Append("setTimeout(function() {");

			script.Indent++;
			script.NewLine();

			script.RenderIdSelector(ClientID);
			script.Append(".dialog(");
			script.AppendValue("close");
			script.Append(");");

			script.Indent--;
			script.NewLine();

			script.Append("}, 0);");
		}

		/// <summary>
		/// Renders client script to initialize the <see cref="Show"/> and <see cref="OnClientOpen"/> properties after initialization so that an <see cref="AutoOpen"/> done for post-back purposes does not appear as one to the end user.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="script">A <see cref="ScriptBuilder"/> that creates the output stream to render jQuery JavaScript on the client.</param>
		/// <seealso cref="Show"/>
		/// <seealso cref="OnClientOpen"/>
		/// <seealso cref="AutoOpen"/>
		protected virtual void RenderDelayedShowInitScript(ScriptBuilder script) {

			string clientId = ClientID;

			DialogAnimation show = Show;
			if(showControl != null || show != defaultShow) {
				script.NewLine();
				script.RenderIdSelector(clientId);
				script.Append(".dialog('option', 'show', ");
				if(showControl != null) script.AppendValue(hideControl.Effect);
				else if(show != defaultShow) script.AppendValue(show);
				script.Append(");");
			}

			ScriptBuilder onOpenScript = new ScriptBuilder();
			onOpenScript.Indent = script.Indent;
			onOpenScript.OmitIndent();

			RenderOpenEventScript(onOpenScript, true);
			if(onOpenScript.Length != 0) {
				script.NewLine();
				script.RenderIdSelector(clientId);
				script.Append(".dialog('option', 'open', ");
				script.Append(onOpenScript.ToString());
				script.Append(");");
			}
		}

		#endregion


		#region Methods: Script helpers

		/// <summary>
		/// Creates jQuery script to open the dialog on the client.
		/// </summary>
		/// <returns>Returns jQuery script to open the dialog on the client.</returns>
		public string GenerateOpenScript() {
			ScriptBuilder script = new ScriptBuilder();
			RenderOpenMethodScript(script);
			return script.ToString();
		}

		/// <summary>
		/// Creates jQuery script to close the dialog on the client.
		/// </summary>
		/// <returns>Returns jQuery script to close the dialog on the client.</returns>
		public string GenerateCloseScript() {
			ScriptBuilder script = new ScriptBuilder();
			RenderCloseMethodScript(script);
			return script.ToString();
		}

		#endregion


		#region Methods: Post data/events

		/// <summary>
		/// Processes postback data for an ASP.NET server control.
		/// This method is used primarily by control developers.
		/// </summary>
		/// <param name="postDataKey">The key identifier for the control.</param>
		/// <param name="postCollection">The collection of all incoming name values.</param>
		/// <returns>True if the server control's state changes as a result of the postback; otherwise, false.</returns>
		protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {

			Page.ClientScript.ValidateEvent(postDataKey);
			bool changed = false;

			bool newIsOpen;
			if(bool.TryParse(postCollection[IsOpenClientId], out newIsOpen) && newIsOpen != IsOpen) {
				isOpen = newIsOpen;
				changed = isOpenChanged = true;
			}

			int? newHeight;
			if(Utility.TryParse(postCollection[HeightClientId], out newHeight) && newHeight != Height) {
				Height = newHeight;
				changed = heightChanged = true;
			}

			Position newPosition;
			if(Position.TryParse(postCollection[PositionClientId], out newPosition) && newPosition != Position) {
				Position = newPosition;
				changed = positionChanged = true;
			}

			int newWidth;
			if(int.TryParse(postCollection[WidthClientId], out newWidth) && newWidth != Width) {
				Width = newWidth;
				changed = widthChanged = true;
			}

			return changed;
		}

		/// <summary>
		/// Signals the server control to notify the ASP.NET application that the state of the control has changed.
		/// This method is used primarily by control developers.
		/// </summary>
		[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This method is amazing.  You get a complaint if you don't include the protected one, but when you do, it tells you to use events.  I included it simply because it gives the derrived control a chance to hook deeper.")]
		protected virtual void RaisePostDataChangedEvent() {
			if(isOpenChanged) OnIsOpenChanged(EventArgs.Empty);
			if(heightChanged) OnHeightChanged(EventArgs.Empty);
			if(positionChanged) OnPositionChanged(EventArgs.Empty);
			if(widthChanged) OnWidthChanged(EventArgs.Empty);
		}

		/// <summary>
		/// Enables a server control to process an event raised when a form is posted to the server.
		/// </summary>
		/// <param name="eventArgument">A <see cref="String"/> that represents an optional event argument to be passed to the event handler.</param>
		[SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", Justification = "This method is amazing.  You get a complaint if you don't include the protected one, but when you do, it tells you to use events.  I included it simply because it gives the derrived control a chance to hook deeper.")]
		protected virtual void RaisePostBackEvent(string eventArgument) {

			Page.ClientScript.ValidateEvent(UniqueID, eventArgument);

			//TODO: Add Postbacks

		}

		#endregion


		#region Explicit Implementation: IPostBackDataHandler

		bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
			return LoadPostData(postDataKey, postCollection);
		}

		void IPostBackDataHandler.RaisePostDataChangedEvent() {
			RaisePostDataChangedEvent();
		}

		#endregion


		#region Explicit Implementation: IPostBackEventHandler

		void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
			RaisePostBackEvent(eventArgument);
		}

		#endregion


	}

}